﻿@model Lokad.Cloud.Console.WebRole.Models.Logs.LogsModel
@{ ViewBag.Title = "Lokad.Cloud Administration Console - Logs"; }

@section Header {
	<script src="http://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js" type="text/javascript"></script>
	<link href="@Url.Content("~/Content/ui.slider.extras.css")" rel="stylesheet" type="text/css" />
	<script src="@Url.Content("~/Scripts/selectToUISlider.jQuery.js")" type="text/javascript"></script>
	<script src="@Url.Content("~/Scripts/knockout-1.1.2.js")" type="text/javascript"></script>
	<script src="@Url.Content("~/Scripts/knockout.mapping.js")" type="text/javascript"></script>
}

<h1>Logs</h1>
<p>Use the slider below to choose the threshold level for filtering out less important log entries.</p>

<fieldset>
	<select name="loglevel" id="loglevel">
		<option value="Debug">Debug</option>
		<option value="Info" selected="selected">Info</option>
		<option value="Warn">Warning</option>
		<option value="Error">Error</option>
		<option value="Fatal">Fatal</option>
	</select>
</fieldset>

<div class="loadMore" data-bind='visible: NewerAvailable, click: loadNewerEntries'>New Entries Available</div>
<div data-bind='template: { name: "logGroupTemplate", foreach: Groups }'></div>

<br />
<p data-bind="visible: Groups().length == 0">No log entries satisfy the chosen log level threshold. Use the level selector above to choose a different level.</p>

<script type="text/html" id="logGroupTemplate">
	<h2>${Title}</h2>
	<table class="table" id="datatable">
	<thead>
		<tr>
			<th width="60">Time</th>
			<th width="20"></th>
			<th width="100%">Message</th>
		</tr>
	</thead>
	<tbody data-bind='template: {name: "logEntryTemplate", foreach: Entries}'></tbody>
	</table>
	<div class="loadMore" data-bind='visible: OlderAvailable, click: function() { loadOlderEntries($data) }'>More Available</div>
</script>

<script type="text/html" id="logEntryTemplate">
	<tr>
		<td>${Time}</td>
		<td><span class="icon-LogLevels icon-LogLevels-${Level}"></span></td>
		<td>
			${Message}<br />
			<div data-bind="visible: Error">
			<b>Exception Details and Stack:</b> (<a href="#" data-bind="click: function() { toggleShowDetails($data) }">Show/Hide</a>)<br />
			<pre data-bind="visible: ShowDetails">${Error}</pre>
			</div>
		</td>
	</tr>
</script>

<script type="text/javascript">

	// inline to save a separate request
	var viewModel = ko.mapping.fromJSON(@Html.Enquote(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model)));

	var updateMask = false;
	var ajaxQueue = $({});
	$.ajaxQueue = function(ajaxOpts) {
		var oldComplete = ajaxOpts.complete;
		ajaxQueue.queue(function(next) {
			ajaxOpts.complete = function() {
				if(oldComplete) { oldComplete.apply(this, arguments); }
				next();
			};
			$.ajax(ajaxOpts);
		});
	};

	toggleShowDetails = function(entry) {
		entry.ShowDetails(!entry.ShowDetails());
	};

	loadOlderEntries = function(group) {
		group.OlderAvailable(false);
		var groupIndex = viewModel.Groups.indexOf(group);
		var newerThanToken = groupIndex == (viewModel.Groups().length - 1) ? undefined : viewModel.Groups()[groupIndex + 1].NewestToken();
		$.ajaxQueue({
			url: '@ViewBag.TenantPath/Entries',
			dataType: 'json',
			data: { threshold: $('select#loglevel').val(), skip: group.Entries().length, count: 50, olderThanToken: group.OldestToken(), newerThanToken: newerThanToken },
			cache: false,
			success: function (data) {
				$.each(data.Groups, function (i, g) { viewModel.Groups.push(ko.mapping.fromJS(g)); });
				sortGroups();
				joinGroups();
			}
		});
	};

	loadNewerEntries = function() {
		viewModel.NewerAvailable(false);
		$.ajaxQueue({
			url: '@ViewBag.TenantPath/Entries',
			dataType: 'json',
			data: { threshold: $('select#loglevel').val(), count: 50, newerThanToken: viewModel.NewestToken() },
			cache: false,
			success: function (data) {
				viewModel.NewerAvailable(false);
				viewModel.NewestToken(data.NewestToken);
				while(group = data.Groups.pop()) { viewModel.Groups.unshift(ko.mapping.fromJS(group)); }
				joinGroups();
			}
		});
	}

	sortGroups = function() {
		viewModel.Groups.sort(function (l,r) {
			if(l.Key() != r.Key()) {
				return l.Key() < r.Key() ? 1 : -1
			}
			if(l.NewestToken() != r.NewestToken()) {
				return l.NewestToken() < r.NewestToken() ? 1 : -1
			}
			return 0;
		});
	}

	joinGroups = function() {
		var groups = viewModel.Groups();
		for(index=groups.length-2; index >= 0; index--) {
			var upper = groups[index];
			var lower = groups[index+1];
			if ((upper.Key() == lower.Key()) && !upper.OlderAvailable()) {
				$.each(lower.Entries(), function (i,e) { upper.Entries.push(e); });
				upper.OlderAvailable(lower.OlderAvailable());
				upper.OldestToken(lower.OldestToken());
				viewModel.Groups.remove(lower);
			}
		}
	}

	$(document).ready(function() {

		ko.applyBindings(viewModel);

		$('#AjaxLoading')
			.bind("ajaxStart", function() { $(this).show(); updateMask = true; })
			.bind("ajaxStop", function() { $(this).hide(); updateMask = false; })

		$('select#loglevel').selectToUISlider({ labels: 5, sliderOptions: { change: function() {
			$.ajaxQueue({
				url: '@ViewBag.TenantPath/Entries',
				dataType: 'json',
				data: { threshold: $('select#loglevel').val() },
				cache: false,
				success: function (data) {
					ko.mapping.updateFromJS(viewModel, data);
				}
			});
		}}}).hide();

		setInterval(function () {
			if (updateMask || viewModel.NewerAvailable()) { return; }
			$.ajaxQueue({
				url: '@ViewBag.TenantPath/HasNewerEntries',
				dataType: 'json',
				data: { threshold: $('select#loglevel').val(), newerThanToken: viewModel.NewestToken() },
				cache: false,
				success: function (data) {
					viewModel.NewerAvailable(data.HasMore);
				}
			});
		}, 30000);
	});
</script>
